home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Image / GraphViz.php < prev    next >
PHP Script  |  2004-03-24  |  12KB  |  431 lines

  1. <?php
  2. //
  3. // +---------------------------------------------------------------------------+
  4. // | PEAR :: Image :: GraphViz                                                 |
  5. // +---------------------------------------------------------------------------+
  6. // | Copyright (c) 2002-2004 Sebastian Bergmann <sb@sebastian-bergmann.de> and |
  7. // |                         Dr. Volker G÷bbels <vmg@arachnion.de>.            |
  8. // +---------------------------------------------------------------------------+
  9. // | This source file is subject to version 3.00 of the PHP License,           |
  10. // | that is available at http://www.php.net/license/3_0.txt.                  |
  11. // | If you did not receive a copy of the PHP license and are unable to        |
  12. // | obtain it through the world-wide-web, please send a note to               |
  13. // | license@php.net so we can mail you a copy immediately.                    |
  14. // +---------------------------------------------------------------------------+
  15. //
  16. // $Id: GraphViz.php,v 1.21 2004/02/25 10:35:57 sebastian Exp $
  17. //
  18.  
  19. require_once 'System.php';
  20.  
  21. /**
  22. * PEAR::Image_GraphViz
  23. *
  24. * Purpose
  25. *
  26. *     Allows for the creation of and the work with directed
  27. *     and undirected graphs and their visualization with
  28. *     AT&T's GraphViz tools. These can be found at
  29. *     http://www.research.att.com/sw/tools/graphviz/
  30. *
  31. * Example
  32. *
  33. *     require_once 'Image/GraphViz.php';
  34. *     $graph = new Image_GraphViz();
  35. *
  36. *     $graph->addNode('Node1', array('URL'      => 'http://link1',
  37. *                                    'label'    => 'This is a label',
  38. *                                    'shape'    => 'box'
  39. *                                    )
  40. *                     );
  41. *     $graph->addNode('Node2', array('URL'      => 'http://link2',
  42. *                                    'fontsize' => '14'
  43. *                                    )
  44. *                     );
  45. *     $graph->addNode('Node3', array('URL'      => 'http://link3',
  46. *                                    'fontsize' => '20'
  47. *                                    )
  48. *                     );
  49. *
  50. *     $graph->addEdge(array('Node1' => 'Node2'), array('label' => 'Edge Label'));
  51. *     $graph->addEdge(array('Node1' => 'Node2'), array('color' => 'red'));
  52. *
  53. *     $graph->image();
  54. *
  55. * @author  Sebastian Bergmann <sb@sebastian-bergmann.de>
  56. *          Dr. Volker G÷bbels <vmg@arachnion.de>
  57. * @package Image
  58. */
  59. class Image_GraphViz {
  60.     /**
  61.     * Path to GraphViz/dot command
  62.     *
  63.     * @var  string
  64.     */
  65.     var $dotCommand = 'dot';
  66.  
  67.     /**
  68.     * Path to GraphViz/neato command
  69.     *
  70.     * @var  string
  71.     */
  72.     var $neatoCommand = 'neato';
  73.  
  74.     /**
  75.     * Representation of the graph
  76.     *
  77.     * @var  array
  78.     */
  79.     var $graph;
  80.  
  81.     /**
  82.     * Constructor
  83.     *
  84.     * @param  boolean Directed (true) or undirected (false) graph.
  85.     * @param  array   Attributes of the graph
  86.     * @access public
  87.     */
  88.     function Image_GraphViz($directed = true, $attributes = array()) {
  89.         $this->setDirected($directed);
  90.         $this->setAttributes($attributes);
  91.     }
  92.  
  93.     /**
  94.     * Output image of the graph in a given format.
  95.     *
  96.     * @param  string  Format of the output image.
  97.     *                 This may be one of the formats supported by GraphViz.
  98.     * @access public
  99.     */
  100.     function image($format = 'svg') {
  101.         if ($file = $this->saveParsedGraph()) {
  102.             $outputfile = $file . '.' . $format;
  103.             $command  = $this->graph['directed'] ? $this->dotCommand : $this->neatoCommand;
  104.             $command .= " -T$format -o$outputfile $file";
  105.  
  106.             @`$command`;
  107.             @unlink($file);
  108.  
  109.             $sendContentLengthHeader = true;
  110.  
  111.             switch ($format) {
  112.                 case 'gif':
  113.                 case 'png':
  114.                 case 'wbmp': {
  115.                     header('Content-Type: image/' . $format);
  116.                 }
  117.                 break;
  118.  
  119.                 case 'jpg': {
  120.                     header('Content-Type: image/jpeg');
  121.                 }
  122.                 break;
  123.  
  124.                 case 'pdf': {
  125.                     header('Content-Type: application/pdf');
  126.                 }
  127.                 break;
  128.  
  129.                 case 'svg': {
  130.                     header('Content-Type: image/svg+xml');
  131.                 }
  132.                 break;
  133.  
  134.                 default: {
  135.                     $sendContentLengthHeader = false;
  136.                 }
  137.             }
  138.  
  139.             if ($sendContentLengthHeader) {
  140.                 header('Content-Length: ' . filesize($outputfile));
  141.             }
  142.  
  143.             $fp = fopen($outputfile, 'rb');
  144.  
  145.             if ($fp) {
  146.                 echo fread($fp, filesize($outputfile));
  147.                 fclose($fp);
  148.                 @unlink($outputfile);
  149.             }
  150.         }
  151.     }
  152.  
  153.     /**
  154.     * Add a cluster to the graph.
  155.     *
  156.     * @param  string  ID.
  157.     * @param  array   Title.
  158.     * @access public
  159.     */
  160.     function addCluster($id, $title) {
  161.         $this->graph['clusters'][$id] = $title;
  162.     }
  163.  
  164.     /**
  165.     * Add a note to the graph.
  166.     *
  167.     * @param  string  Name of the node.
  168.     * @param  array   Attributes of the node.
  169.     * @param  string  Group of the node.
  170.     * @access public
  171.     */
  172.     function addNode($name, $attributes = array(), $group = 'default') {
  173.         $this->graph['nodes'][$group][$name] = $attributes;
  174.     }
  175.  
  176.     /**
  177.     * Remove a node from the graph.
  178.     *
  179.     * @param  Name of the node to be removed.
  180.     * @access public
  181.     */
  182.     function removeNode($name, $group = 'default') {
  183.         if (isset($this->graph['nodes'][$group][$name])) {
  184.             unset($this->graph['nodes'][$group][$name]);
  185.         }
  186.     }
  187.  
  188.     /**
  189.     * Add an edge to the graph.
  190.     *
  191.     * @param  array Start and End node of the edge.
  192.     * @param  array Attributes of the edge.
  193.     * @access public
  194.     */
  195.     function addEdge($edge, $attributes = array()) {
  196.         if (is_array($edge)) {
  197.             $from = key($edge);
  198.             $to   = $edge[$from];
  199.             $id   = $from . '_' . $to;
  200.  
  201.             if (!isset($this->graph['edges'][$id])) {
  202.                 $this->graph['edges'][$id] = $edge;
  203.             } else {
  204.                 $this->graph['edges'][$id] = array_merge(
  205.                   $this->graph['edges'][$id],
  206.                   $edge
  207.                 );
  208.             }
  209.  
  210.             if (is_array($attributes)) {
  211.                 if (!isset($this->graph['edgeAttributes'][$id])) {
  212.                     $this->graph['edgeAttributes'][$id] = $attributes;
  213.                 } else {
  214.                     $this->graph['edgeAttributes'][$id] = array_merge(
  215.                       $this->graph['edgeAttributes'][$id],
  216.                       $attributes
  217.                     );
  218.                 }
  219.             }
  220.         }
  221.     }
  222.  
  223.     /**
  224.     * Remove an edge from the graph.
  225.     *
  226.     * @param  array Start and End node of the edge to be removed.
  227.     * @access public
  228.     */
  229.     function removeEdge($edge) {
  230.         if (is_array($edge)) {
  231.               $from = key($edge);
  232.               $to   = $edge[$from];
  233.               $id   = $from . '_' . $to;
  234.  
  235.             if (isset($this->graph['edges'][$id])) {
  236.                 unset($this->graph['edges'][$id]);
  237.             }
  238.  
  239.             if (isset($this->graph['edgeAttributes'][$id])) {
  240.                 unset($this->graph['edgeAttributes'][$id]);
  241.             }
  242.         }
  243.     }
  244.  
  245.     /**
  246.     * Add attributes to the graph.
  247.     *
  248.     * @param  array Attributes to be added to the graph.
  249.     * @access public
  250.     */
  251.     function addAttributes($attributes) {
  252.         if (is_array($attributes)) {
  253.             $this->graph['attributes'] = array_merge(
  254.               $this->graph['attributes'],
  255.               $attributes
  256.             );
  257.         }
  258.     }
  259.  
  260.     /**
  261.     * Set attributes of the graph.
  262.     *
  263.     * @param  array Attributes to be set for the graph.
  264.     * @access public
  265.     */
  266.     function setAttributes($attributes) {
  267.         if (is_array($attributes)) {
  268.             $this->graph['attributes'] = $attributes;
  269.         }
  270.     }
  271.  
  272.     /**
  273.     * Set directed/undirected flag for the graph.
  274.     *
  275.     * @param  boolean Directed (true) or undirected (false) graph.
  276.     * @access public
  277.     */
  278.     function setDirected($directed) {
  279.         if (is_bool($directed)) {
  280.             $this->graph['directed'] = $directed;
  281.         }
  282.     }
  283.  
  284.     /**
  285.     * Load graph from file.
  286.     *
  287.     * @param  string  File to load graph from.
  288.     * @access public
  289.     */
  290.     function load($file) {
  291.         if ($serialized_graph = implode('', @file($file))) {
  292.             $this->graph = unserialize($serialized_graph);
  293.         }
  294.     }
  295.  
  296.     /**
  297.     * Save graph to file.
  298.     *
  299.     * @param  string  File to save the graph to.
  300.     * @return mixed   File the graph was saved to, false on failure.
  301.     * @access public
  302.     */
  303.     function save($file = '') {
  304.         $serialized_graph = serialize($this->graph);
  305.  
  306.         if (empty($file)) {
  307.             $file = System::mktemp('graph_');
  308.         }
  309.  
  310.         if ($fp = @fopen($file, 'w')) {
  311.             @fputs($fp, $serialized_graph);
  312.             @fclose($fp);
  313.  
  314.             return $file;
  315.         }
  316.  
  317.         return false;
  318.     }
  319.  
  320.     /**
  321.     * Parse the graph into GraphViz markup.
  322.     *
  323.     * @return string  GraphViz markup
  324.     * @access public
  325.     */
  326.     function parse() {
  327.         $parsedGraph = "digraph G {\n";
  328.  
  329.         if (isset($this->graph['attributes'])) {
  330.             foreach ($this->graph['attributes'] as $key => $value) {
  331.                 $attributeList[] = $key . '="' . $value . '"';
  332.             }
  333.  
  334.             if (!empty($attributeList)) {
  335.               $parsedGraph .= implode(',', $attributeList) . ";\n";
  336.             }
  337.         }
  338.  
  339.         if (isset($this->graph['nodes'])) {
  340.             foreach($this->graph['nodes'] as $group => $nodes) {
  341.                 if ($group != 'default') {
  342.                   $parsedGraph .= sprintf(
  343.                     "subgraph \"cluster_%s\" {\nlabel=\"%s\";\n",
  344.  
  345.                     $group,
  346.                     isset($this->graph['clusters'][$group]) ? $this->graph['clusters'][$group] : ''
  347.                   );
  348.                 }
  349.  
  350.                 foreach($nodes as $node => $attributes) {
  351.                     unset($attributeList);
  352.  
  353.                     foreach($attributes as $key => $value) {
  354.                         $attributeList[] = $key . '="' . $value . '"';
  355.                     }
  356.  
  357.                     if (!empty($attributeList)) {
  358.                         $parsedGraph .= sprintf(
  359.                           "\"%s\" [ %s ];\n",
  360.                           addslashes(stripslashes($node)),
  361.                           implode(',', $attributeList)
  362.                         );
  363.                     }
  364.                 }
  365.  
  366.                 if ($group != 'default') {
  367.                   $parsedGraph .= "}\n";
  368.                 }
  369.             }
  370.         }
  371.  
  372.         if (isset($this->graph['edges'])) {
  373.             foreach($this->graph['edges'] as $label => $node) {
  374.                 unset($attributeList);
  375.  
  376.                 $from = key($node);
  377.                 $to   = $node[$from];
  378.  
  379.                 foreach($this->graph['edgeAttributes'][$label] as $key => $value) {
  380.                     $attributeList[] = $key . '="' . $value . '"';
  381.                 }
  382.  
  383.                 $parsedGraph .= sprintf(
  384.                   '"%s" -> "%s"',
  385.                   addslashes(stripslashes($from)),
  386.                   addslashes(stripslashes($to))
  387.                 );
  388.                 
  389.                 if (!empty($attributeList)) {
  390.                     $parsedGraph .= sprintf(
  391.                       ' [ %s ]',
  392.                       implode(',', $attributeList)
  393.                     );
  394.                 }
  395.  
  396.                 $parsedGraph .= ";\n";
  397.             }
  398.         }
  399.  
  400.         return $parsedGraph . "}\n";
  401.     }
  402.  
  403.     /**
  404.     * Save GraphViz markup to file.
  405.     *
  406.     * @param  string  File to write the GraphViz markup to.
  407.     * @return mixed   File to which the GraphViz markup was
  408.     *                 written, false on failure.
  409.     * @access public
  410.     */
  411.     function saveParsedGraph($file = '') {
  412.         $parsedGraph = $this->parse();
  413.  
  414.         if (!empty($parsedGraph)) {
  415.             if (empty($file)) {
  416.                 $file = System::mktemp('graph_');
  417.             }
  418.  
  419.             if ($fp = @fopen($file, 'w')) {
  420.                 @fputs($fp, $parsedGraph);
  421.                 @fclose($fp);
  422.  
  423.                 return $file;
  424.             }
  425.         }
  426.  
  427.         return false;
  428.     }
  429. }
  430. ?>
  431.